Jenkins for Beginners - Spring Boot Development Guide
What is Jenkins?โ
Jenkins is an open-source automation server that enables Continuous Integration (CI) and Continuous Deployment (CD) for software development. It helps automate the building, testing, and deployment of Spring Boot applications.
Why Jenkins for Spring Boot?โ
- Automation: Automatically build and test your Spring Boot apps on code changes
- Integration: Works seamlessly with Maven/Gradle, Git, Docker
- Flexibility: Extensive plugin ecosystem
- Scalability: Can handle multiple projects and environments
- Free: Open-source with strong community support
Core Jenkins Conceptsโ
Key Terminologyโ
- Job/Project: A runnable task in Jenkins (e.g., build Spring Boot app)
- Build: Single execution of a job
- Workspace: Directory where Jenkins runs your job
- Node/Agent: Machine where Jenkins executes jobs
- Pipeline: Code-based job definition using Groovy
- Plugin: Extension that adds functionality to Jenkins
Jenkins Architectureโ
โโโโโโโโโโโโโโโโโโโ
โ Jenkins Master โ โ Web UI, Job scheduling, Plugin management
โโโโโโโโโโโโโโโโโโโค
โ Jenkins Agents โ โ Execute jobs, can be on different machines
โโโโโโโโโโโโโโโโโโโค
โ Source Code โ โ Git repositories
โโโโโโโโโโโโโโโโโโโค
โ Artifacts โ โ JAR files, Docker images
โโโโโโโโโโโโโโโโโโโ
Jenkins Installationโ
Local Installation (Development)โ
Option 1: Download WAR Fileโ
# Download Jenkins WAR
wget https://get.jenkins.io/war-stable/latest/jenkins.war
# Run Jenkins
java -jar jenkins.war --httpPort=8080
# Access: http://localhost:8080
Option 2: Docker (Recommended for beginners)โ
# Pull Jenkins image
docker pull jenkins/jenkins:lts
# Run Jenkins container
docker run -d \
--name jenkins \
-p 8080:8080 \
-p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts
# Access: http://localhost:8080
Option 3: Docker Composeโ
# docker-compose.yml
version: '3.8'
services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
ports:
- '8080:8080'
- '50000:50000'
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
environment:
- JENKINS_OPTS="--httpPort=8080"
restart: unless-stopped
volumes:
jenkins_home:
Initial Setup Stepsโ
- Access Jenkins: Navigate to
http://localhost:8080 - Unlock Jenkins: Use initial admin password from logs
- Install Plugins: Choose "Install suggested plugins"
- Create Admin User: Set up your admin account
- Configure Instance: Set Jenkins URL
Essential Plugins for Spring Boot Developmentโ
Must-Have Pluginsโ
# Core plugins for Spring Boot
- Git Plugin # Git integration
- Maven Integration Plugin # Maven support
- Gradle Plugin # Gradle support
- JUnit Plugin # Test result visualization
- Jacoco Plugin # Code coverage
- SonarQube Scanner # Code quality
- Docker Plugin # Docker integration
- Blue Ocean # Modern UI
- Pipeline Plugin # Pipeline as Code
- Credentials Plugin # Secure credential storage
- SSH Agent Plugin # SSH key management
Installation via UIโ
- Manage Jenkins โ Manage Plugins
- Available tab โ Search for plugins
- Select plugins โ Install without restart
Installation via Plugin Managerโ
// plugins.txt (for automated installation)
git:latest
maven-plugin:latest
gradle:latest
junit:latest
jacoco:latest
sonar:latest
docker-plugin:latest
blueocean:latest
workflow-aggregator:latest
Creating Your First Spring Boot Jobโ
Freestyle Project (Beginner-Friendly)โ
Step 1: Create New Jobโ
- New Item โ Enter name:
springboot-hello-world - Select Freestyle project โ OK
Step 2: Configure Source Code Managementโ
# Git Configuration
Repository URL: https://github.com/yourusername/spring-boot-demo.git
Credentials: Add your Git credentials
Branch: */main
Step 3: Build Triggersโ
# Options:
โ GitHub hook trigger for GITScm polling # Webhook-based
โ Poll SCM: H/5 * * * * # Every 5 minutes
โ Build periodically: H 2 * * * # Daily at 2 AM
Step 4: Build Environmentโ
โ Delete workspace before build starts
โ Add timestamps to the Console Output
Step 5: Build Stepsโ
# Add Build Step โ Invoke top-level Maven targets
Goals: clean compile test package
Advanced:
- Maven Version: (Default)
- Settings file: settings.xml (if needed)
- Global Settings: (Default)
Step 6: Post-Build Actionsโ
# Add post-build action โ Archive the artifacts
Files to archive: target/*.jar
# Add post-build action โ Publish JUnit test result report
Test report XMLs: target/surefire-reports/*.xml
# Add post-build action โ Record jacoco coverage report
Path to exec files: target/jacoco.exec
Class Dirs: target/classes
Source Dirs: src/main/java
Sample Spring Boot Application Structureโ
spring-boot-demo/
โโโ pom.xml
โโโ src/
โ โโโ main/
โ โ โโโ java/
โ โ โ โโโ com/example/demo/
โ โ โ โโโ DemoApplication.java
โ โ โโโ resources/
โ โ โโโ application.properties
โ โโโ test/
โ โโโ java/
โ โโโ com/example/demo/
โ โโโ DemoApplicationTests.java
โโโ Jenkinsfile (for Pipeline)
Jenkins Pipelines for Spring Bootโ
Declarative Pipeline (Recommended for Beginners)โ
Basic Jenkinsfileโ
pipeline {
agent any
tools {
maven 'Maven-3.9'
jdk 'JDK-17'
}
environment {
SPRING_PROFILES_ACTIVE = 'test'
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'https://github.com/yourusername/spring-boot-demo.git'
}
}
stage('Build') {
steps {
sh 'mvn clean compile'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
publishTestResults testResultsPattern: 'target/surefire-reports/*.xml'
publishCoverage adapters: [
jacocoAdapter('target/site/jacoco/jacoco.xml')
]
}
}
}
stage('Package') {
steps {
sh 'mvn package -DskipTests'
}
post {
success {
archiveArtifacts artifacts: 'target/*.jar',
fingerprint: true
}
}
}
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('SonarQube') {
sh 'mvn sonar:sonar'
}
}
}
stage('Deploy to Staging') {
when {
branch 'develop'
}
steps {
echo 'Deploying to staging environment...'
sh 'java -jar target/*.jar --server.port=8081 &'
sh 'sleep 30' // Wait for app to start
sh 'curl -f http://localhost:8081/actuator/health || exit 1'
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
input message: 'Deploy to production?', ok: 'Deploy'
echo 'Deploying to production environment...'
// Add production deployment steps
}
}
}
post {
always {
cleanWs()
}
success {
emailext (
subject: "โ
Build Success: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Build succeeded: ${env.BUILD_URL}",
to: "developer@company.com"
)
}
failure {
emailext (
subject: "โ Build Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Build failed: ${env.BUILD_URL}",
to: "developer@company.com"
)
}
}
}
Advanced Pipeline with Dockerโ
pipeline {
agent any
environment {
DOCKER_IMAGE = "myapp"
DOCKER_TAG = "${BUILD_NUMBER}"
REGISTRY_URL = "your-registry.com"
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build & Test') {
parallel {
stage('Maven Build') {
steps {
sh 'mvn clean package'
}
}
stage('Unit Tests') {
steps {
sh 'mvn test'
}
post {
always {
publishTestResults testResultsPattern: 'target/surefire-reports/*.xml'
}
}
}
stage('Integration Tests') {
steps {
sh 'mvn verify -P integration-tests'
}
}
}
}
stage('Code Quality') {
parallel {
stage('SonarQube') {
steps {
withSonarQubeEnv('SonarQube') {
sh 'mvn sonar:sonar'
}
}
}
stage('Security Scan') {
steps {
sh 'mvn org.owasp:dependency-check-maven:check'
}
}
}
}
stage('Docker Build') {
steps {
script {
def image = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
docker.withRegistry("https://${REGISTRY_URL}", 'docker-registry-credentials') {
image.push()
image.push('latest')
}
}
}
}
stage('Deploy') {
when {
anyOf {
branch 'main'
branch 'develop'
}
}
steps {
script {
def environment = env.BRANCH_NAME == 'main' ? 'production' : 'staging'
sh "docker run -d --name ${environment}-${BUILD_NUMBER} -p 808${BUILD_NUMBER % 10}:8080 ${DOCKER_IMAGE}:${DOCKER_TAG}"
// Health check
sh "sleep 30"
sh "curl -f http://localhost:808${BUILD_NUMBER % 10}/actuator/health"
}
}
}
}
post {
always {
// Clean up Docker containers
sh "docker stop \$(docker ps -aq) || true"
sh "docker rm \$(docker ps -aq) || true"
cleanWs()
}
}
}
Multi-Branch Pipeline Setupโ
Step 1: Create Multi-Branch Pipelineโ
- New Item โ Multibranch Pipeline
- Branch Sources โ Git
- Project Repository: Your Git URL
- Credentials: Your Git credentials
- Scan Multibranch Pipeline Triggers: Periodically
Step 2: Branch Strategyโ
// Jenkinsfile - Branch-specific deployment
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'mvn clean package'
}
}
stage('Deploy') {
parallel {
stage('Deploy to Dev') {
when { branch 'feature/*' }
steps {
echo "Deploying feature branch to dev environment"
}
}
stage('Deploy to Staging') {
when { branch 'develop' }
steps {
echo "Deploying to staging environment"
}
}
stage('Deploy to Production') {
when { branch 'main' }
steps {
input message: 'Deploy to production?'
echo "Deploying to production environment"
}
}
}
}
}
}
Jenkins Configuration for Spring Bootโ
Global Tool Configurationโ
- Manage Jenkins โ Global Tool Configuration
Maven Configurationโ
Name: Maven-3.9
Install automatically: โ
Version: 3.9.0
JDK Configurationโ
Name: JDK-17
Install automatically: โ
Add Installer: Install from adoptium.net
Version: jdk-17.0.7+7
Git Configurationโ
Name: Default
Path to Git executable: git (or /usr/bin/git)
System Configurationโ
- Manage Jenkins โ Configure System
Global Propertiesโ
Environment variables:
- JAVA_HOME: /opt/java/openjdk
- MAVEN_HOME: /opt/maven
- PATH: $MAVEN_HOME/bin:$JAVA_HOME/bin:$PATH
Spring Boot Specific Configurationsโ
Maven Settings for Jenkinsโ
<!-- settings.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0">
<profiles>
<profile>
<id>jenkins</id>
<properties>
<spring.profiles.active>jenkins</spring.profiles.active>
<skipTests>false</skipTests>
</properties>
</profile>
</profiles>
<activeProfiles>
<activeProfile>jenkins</activeProfile>
</activeProfiles>
</settings>
Application Properties for Testingโ
# application-jenkins.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# Disable security for testing
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
# Logging
logging.level.com.example=DEBUG
logging.pattern.console=%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
POM.xml Configuration for Jenkinsโ
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spring-boot.version>3.1.0</spring-boot.version>
<jacoco.version>0.8.8</jacoco.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Surefire for unit tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testFailureIgnore>false</testFailureIgnore>
<skipTests>${skipTests}</skipTests>
</configuration>
</plugin>
<!-- Failsafe for integration tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- JaCoCo for code coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Testing Strategies in Jenkinsโ
Unit Tests with JUnitโ
stage('Unit Tests') {
steps {
sh 'mvn clean test'
}
post {
always {
publishTestResults testResultsPattern: 'target/surefire-reports/*.xml'
publishCoverage adapters: [
jacocoAdapter('target/site/jacoco/jacoco.xml')
], sourceFileResolver: sourceFiles('STORE_LAST_BUILD')
}
}
}
Integration Testsโ
stage('Integration Tests') {
steps {
sh 'mvn verify -P integration-tests'
}
post {
always {
publishTestResults testResultsPattern: 'target/failsafe-reports/*.xml'
}
}
}
Database Testingโ
pipeline {
agent any
services {
postgres {
image 'postgres:13'
environment {
POSTGRES_PASSWORD = 'test'
POSTGRES_DB = 'testdb'
}
}
}
stages {
stage('Database Tests') {
environment {
SPRING_DATASOURCE_URL = 'jdbc:postgresql://postgres:5432/testdb'
SPRING_DATASOURCE_USERNAME = 'postgres'
SPRING_DATASOURCE_PASSWORD = 'test'
}
steps {
sh 'mvn test -P database-tests'
}
}
}
}
Deployment Strategiesโ
Simple Deploymentโ
stage('Deploy') {
steps {
sh '''
# Stop existing application
pkill -f "spring-boot-demo" || true
# Deploy new version
nohup java -jar target/*.jar --server.port=8080 > app.log 2>&1 &
# Health check
sleep 30
curl -f http://localhost:8080/actuator/health
'''
}
}
Docker Deploymentโ
stage('Docker Deploy') {
steps {
script {
// Build image
def image = docker.build("myapp:${BUILD_NUMBER}")
// Stop existing container
sh 'docker stop myapp-container || true'
sh 'docker rm myapp-container || true'
// Run new container
sh """
docker run -d \
--name myapp-container \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=production \
myapp:${BUILD_NUMBER}
"""
// Health check
sh 'sleep 30'
sh 'curl -f http://localhost:8080/actuator/health'
}
}
}
Blue-Green Deploymentโ
stage('Blue-Green Deploy') {
steps {
script {
def newPort = env.CURRENT_PORT == '8080' ? '8081' : '8080'
// Deploy to new port
sh """
docker run -d \
--name myapp-${newPort} \
-p ${newPort}:8080 \
myapp:${BUILD_NUMBER}
"""
// Health check
sh "sleep 30"
sh "curl -f http://localhost:${newPort}/actuator/health"
// Switch traffic (update load balancer)
sh "echo 'Switching traffic to port ${newPort}'"
// Stop old version
sh "docker stop myapp-${env.CURRENT_PORT} || true"
// Update current port
env.CURRENT_PORT = newPort
}
}
}
Monitoring and Notificationsโ
Email Notificationsโ
post {
always {
emailext (
subject: "Build ${currentBuild.result}: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: """
Build ${currentBuild.result}
Job: ${env.JOB_NAME}
Build: ${env.BUILD_NUMBER}
URL: ${env.BUILD_URL}
Changes:
${env.CHANGE_LOG}
""",
recipientProviders: [
[$class: 'DevelopersRecipientProvider'],
[$class: 'RequesterRecipientProvider']
]
)
}
}
Slack Integrationโ
post {
success {
slackSend(
channel: '#deployments',
color: 'good',
message: "